<?php
abstract class DataModel
{
    const
        BOOLEAN  = 'boolean'
      , INTEGER  = 'integer'
      , BIGINT   = 'bigint'
      , DOUBLE   = 'double'
      , FLOAT    = 'double'
      , TEXT     = 'text'
      , DATE     = 'date'
      , DATETIME = 'dateTime'
      ;

    protected $_data = array();
    protected $_schema = array();
    protected $_extra = array();

    function __get($prop) {
        if (isset($this->_data[$prop])) {
            return $this->_data[$prop];
        } elseif (isset($this->_schema[$prop])) {
            return null;
        } else if ( strpos($prop, '_') === 0 ) {
            return @$this->_extra[$prop];
        } else {
            throw new InvalidArgumentException;
        }
    }

    function __isset($prop) {
        if ( strpos($prop, '_') === 0 )
            return isset($this->_extra[$prop]);
        return isset($this->_data[$prop]);
    }

    function __set($prop, $val) {
 
        if ( strpos($prop, '_') === 0 ) {
            $this->_extra[$prop] = $val;
            return;
            
        } else if (!isset($this->_schema[$prop])) {
            throw new InvalidArgumentException($prop.'はセットできません');
            return;
        }

        $schema = $this->_schema[$prop];
        $type = gettype($val);
        
        if ($schema === self::DATETIME) {
            if ($val instanceof DateTime) {
                $this->_data[$prop] = $val;
            } else {
                if ($val) $this->_data[$prop] = new DateTime($val);
                
            }
            return;
        }

        if ($type === $schema) {
            $this->_data[$prop] = $val;
            return;
        }

        switch ($schema) {
            case self::BOOLEAN:
                return $this->_data[$prop] = (bool)$val;
            case self::INTEGER:
                return $this->_data[$prop] = (int)$val;
            case self::DOUBLE:
                return $this->_data[$prop] = (double)$val;
            case self::TEXT:
            default:
                if (is_array($val)) return $this->_data[$prop] = $val;
                return $this->_data[$prop] = (string)$val;
        }
    }

    function isProperty($prop) {
        return (isset($this->_schema[$prop]));
    }

    function toArray() {
        return $this->_data;
    }

    function fromArray(array $arr) {
        foreach ($arr as $key => $val) {
            if ($key == '_category' && get_class($this) == 'Category') {
                $ret = array();
                foreach ($val as $k => $v) {
                    $data = new Category;
                    $data->fromArray($v);
                    $ret[] = $data;
                }
                $this->__set($key, $ret);
            }
            else if (! in_array($key, array('confirm','submit','delete','edit','login')) ) {
                $this->__set($key, $val);
            }
        }
    }

    abstract function isValid(Array $error = null);



    static function checkId($val)
    {
        return ((empty($val) && $val != 0) || !preg_match("/^[0-9]+$/", $val));
    }

    static function checkLength($val, $max, $min)
    {
        if ($min) return (empty($val) || !mb_check_encoding($val) || mb_strlen($val) > $max || mb_strlen($val) < $min);
        return ( !mb_check_encoding($val) || mb_strlen($val) > $max || mb_strlen($val) < $min);
    }

    static function checkLowerAlphabet($val)
    {
        return (mb_ereg('[^a-z]', $val) );
    }

    static function checkKana($val)
    {
        return (mb_ereg('[^ぁ-ん_ー]', $val) );
    }

    static function checkNumber($val, $notZero)
    {
        if ($notZero) return ((empty($val) && $val != 0) || !preg_match("/^[0-9]+$/", $val));
        return (empty($val) || !preg_match("/^[0-9]+$/", $val));
    }
    
    static function checkMail($val)
    {
        if (!$val) return 1;
        $val = rtrim($val);
        return (!preg_match("/^([a-zA-Z0-9])+([a-zA-Z0-9\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/", $val));
    }

    static function checkLimitedCharacters ($val, $array) {
        return (!in_array($val, $array));
    }

    static function checkTrueDay ($val) {
        $val = str_replace('-', '', $val);
        $m = substr($val, 4, 2);
        $d = substr($val, 6, 2);
        $y = substr($val, 0, 4);
        return (!checkdate($m, $d, $y) );
    }

    static function checkFuture ($val) {
        $val = str_replace('-', '', $val);
        $future = date( "Ymd", time() );
        return ($val > $future);
    }
}

/*
 *     private function chNotNull($v) {
        if (!$v)
            return 1;
    }
    private function chLimitedCharacters ($v, $array) {
    }
    private function chNumAlpha ($v) {
        if (mb_ereg('[^0-9a-zA-Z]', $v) )
            return 1;
    }
    private function chNumber ($v) {
        if (mb_ereg('[^0-9]', $v) )
            return 1;
    }

    private function chMinNumber ($v, $min) {
        if ($v < $min)
            return 1;
    }
    private function chListBox ($v, $min) {
        if ($v < $min)
            return 1;
    }
    private function chTrueDate ($v, $day = false) {
        $m = substr($v, 4, 2);
        $d = ($day) ? substr($v, 6, 2) : 1;
        $y = substr($v, 0, 4);
        if (!checkdate($m, $d, $y) )
            return 1;
    }
    private function chToTheFuture($v, $future = 0) {
    }*/